/*
 * Fall 2008: Computer Graphics B581
 * Eran Chinthaka - Final Project
 */

#include <stdio.h>
#include <stdlib.h>
// #include <unistd.h>
#include <math.h>

#ifdef WIN32
#include <windows.h>
#endif

#if defined (__APPLE__) || defined(MACOSX)
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#include "readoff.h"
#include "readppm.h"
#include "ChessPiece.h"

/*****************************************************************/
/******************** MY FUNCTION DEFINITIONS ********************/
/*****************************************************************/

static void myInitGL(void);
static void myInitData(int *argc, char **argv);
static void myInitMenus(void);
static void _Timer(int);
void drawChessBoard(GLenum);
void processHits(GLint hits, GLuint buffer[]);
void mousePickSquares(int button, int state, int x, int y);

/* my callback functions needed for GLUT event handling */
static void myHandleMenu(int value);
static void myDisplay(void);
static void myReshape(int width, int height);
static void myMotion(int curx, int cury);
static void myButton(int button, int state, int x, int y);
static void myVisibility(int state);
static void myKeyboard(unsigned char key, int x, int y);
static void myMenus(int value);

/***************************************************************/
/******************** MY GLOBAL CONSTANTS **********************/
/***************************************************************/

#define MY_MAX(A,B) ((A) >= (B) ? (A) : (B))

/* my constants for GLUT window defaults */
#define MY_WIND_X_SIZE     600
#define MY_WIND_Y_SIZE     400
#define MY_WIND_X_POS      100
#define MY_WIND_Y_POS      100

/* my menu IDs for GLUT menu-handling functions */
#define MY_MENU_EXIT     -9999
#define MY_MENU_RESET     -200
#define MY_MENU_CAMERA      20
#define MY_MENU_LIGHT       30
#define MY_MENU_OBJECT      40
#define MY_MENU_PROJECT     100
#define MY_MENU_VIEW_PLAYER_ONE 201
#define MY_MENU_VIEW_PLAYER_TWO 202
#define MY_MENU_VIEW_SIDE_VIEW 203
#define MY_MENU_VIEW_PLAN_VIEW 204

#define MY_MENU_OBJECT_ROTATE_Y 301
#define MY_MENU_OBJECT_ROTATE_Z 302
#define MY_MENU_OBJECT_ROTATE_X 303
#define MY_MENU_OBJECT_TRANSLATE 304

#define MY_MENU_PLAY 400

#define	MY_CHECKERS_WIDTH   64
#define	MY_CHECKERS_HEIGHT  64

#define CHESS_PAWN 1
#define CHESS_KING 2
#define CHESS_ROOK 3
#define CHESS_QUEEN 4
#define CHESS_KNIGHT 5
#define CHESS_BISHOP 6
#define WHITE_PIECE 10
#define BLACK_PIECE 20
#define EMPTY_LOCATION 100
#define BUFSIZE 512

/***************************************************************/
/******************** MY GLOBAL VARIABLEt **********************/
/***************************************************************/

static GLuint TextureMode = 1;

GLdouble boxSize = 0.4;
GLdouble chessBoardStartX = 0.0, chessBoardStartZ = -0.8;
Point3 chessBoardCenterOfMass;

static GLfloat LightPos[4];

/* user interaction */
static GLint InteractionMode;
static GLint XmouseDown, YmouseDown;
static GLint XmouseCurrent, YmouseCurrent;
static GLint XmousePrev, YmousePrev, DX, DY;
GLdouble dx, dy, dr, rx, ry;
static GLint WindHeight, WindWidth;
static GLint KeybModifiers;
static GLint mouseButtons = 0;

/* transformations */
static GLdouble TX, TY, TZ;

/* 3D Projection */
static GLdouble XCamera, YCamera, ZCamera;
static GLdouble FOVangle, FOVratio, ClipNear, ClipFar, zRotationAngle = 0.0,
		xRotationAngle = 0, rotationAngle = 0.0, autoRotatingAngle = 60.0;

// this will hold which piece is in a given row column
static GLuint chessBoardLayout[8][8];

// this will hold whether the piece in that location is black or white
static GLuint chessBoardStandings[8][8];

static GLuint selectedPieceRow = 0, selectedPieceColumn = 0, sourceLocationRow =
		3, sourceLocationColumn = 3, targetLocationRow = -1,
		targetLocationColumn = -1;
static GLboolean pieceSelected= GL_FALSE, pieceSelectedToBeMoved= GL_FALSE;

ChessPiece king("off/Chess1a.off");
ChessPiece rook("off/Chess2a.off");
ChessPiece pawn("off/Chess4a.off");
ChessPiece queen("off/Chess3a.off");
ChessPiece bishop("off/m1600.off");
ChessPiece knight("off/m1606.off");

TexMap TMap1, TMap2;
GLuint Texture1, Texture2;

/**************************** MAIN *****************************/

int main(int argc, char **argv) {
	glutInit(&argc, argv);

	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowPosition(MY_WIND_X_POS, MY_WIND_Y_POS);
	glutInitWindowSize(MY_WIND_X_SIZE, MY_WIND_Y_SIZE);

	glutCreateWindow(":: Eran Chinthaka - Chess Game Simulation ::");

	glutDisplayFunc(myDisplay);
	glutReshapeFunc(myReshape);
	glutMouseFunc(myButton);
	glutMotionFunc(myMotion);
	glutPassiveMotionFunc(NULL);
	glutIdleFunc(NULL);
	glutKeyboardFunc(myKeyboard);
	glutVisibilityFunc(myVisibility);

	fprintf(stderr,"Press ESC or 'q' to Exit.\n");

	myInitGL();
	myInitData(&argc, argv); /* pass command line arguments for texture */

	/* initialize GLUT menu handler as last one, to take over access
	 to GLUT_RIGHT_BUTTON, regardles to what other handlers did to it: */
	myInitMenus();

	//	glutTimerFunc(10, _Timer, 0);

	glutMainLoop();
	return (EXIT_SUCCESS);
} /* main() */

/* --------------------------------------- */
/*           initialize my data            */
/* --------------------------------------- */

void loadTexture(char* fileName, GLuint *textureName, TexMap *textMap) {

	int i = ReadPPM(fileName, textMap);
	if (i> 0) {
		printf("Using texture from file.\n"); /* image file is valid: use it as texture */
		glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
		glGenTextures(1, textureName);
		glBindTexture(GL_TEXTURE_2D, *textureName);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textMap->texc, textMap->texr,
		0, GL_RGB, GL_UNSIGNED_BYTE, textMap->texture);
	}
}

void myInitData(int *argc, char **argv) {

	fprintf(stderr, "Initializing ... \n");
	InteractionMode = MY_MENU_PLAY;
	TX = 1.16;
	TY = 1.12;
	TZ = -2.43;

	XmouseDown = -1;
	YmouseDown = -1;
	XmouseCurrent = XmousePrev = -1;
	YmouseCurrent = YmousePrev = -1;
	DX = DY = 0;
	KeybModifiers = 0;

	/* load image from URL specified on command line */

	// init chess board layout
	int x =0, y = 0;
	for (x = 0; x < 8; x++) {
		for (y = 0; y < 8; y++) {
			chessBoardLayout[x][y] = EMPTY_LOCATION;

			if (x == 0 || x == 1) {
				chessBoardStandings[x][y] = WHITE_PIECE;
			} else if (x == 6 || x == 7) {
				chessBoardStandings[x][y] = BLACK_PIECE;
			} else {
				chessBoardStandings[x][y] = EMPTY_LOCATION;
			}
		}
	}

	fprintf(stderr, "Setting initial chess board layout ...\n");
	// set the initial layout

	// 1. set pawns
	for (x = 0; x < 8; x++) {
		chessBoardLayout[1][x] = CHESS_PAWN;
		chessBoardLayout[6][x] = CHESS_PAWN;
	}

	// 2. set rook
	chessBoardLayout[0][0] = chessBoardLayout[0][7] = chessBoardLayout[7][0]
			= chessBoardLayout[7][7] = CHESS_ROOK;
	// 3. set knights
	chessBoardLayout[0][1] = chessBoardLayout[0][6] = chessBoardLayout[7][1]
			= chessBoardLayout[7][6] = CHESS_KNIGHT;
	// 4. set bishops
	chessBoardLayout[0][2] = chessBoardLayout[0][5] = chessBoardLayout[7][2]
			= chessBoardLayout[7][5] = CHESS_BISHOP;
	// 5. set queen
	chessBoardLayout[0][3] = chessBoardLayout[7][3] = CHESS_QUEEN;
	// 6. set king
	chessBoardLayout[0][4] = chessBoardLayout[7][4] = CHESS_KING;

	queen.setCenterLocations(0.1, 0.75, 0.12);
	rook.setCenterLocations(0.1, 0.53, 0.1);
	pawn.setCenterLocations(0.05, 0.57, 0.12);
	king.setCenterLocations(0.1, 0.7, 0.12);
	bishop.setCenterLocations(0.0, 0.1, 0.0);
	knight.setCenterLocations(-0.25, 0.2, -0.1);

	fprintf(stderr, "Downloading texture files ...\n");
	//dowload all the texture files
	system("./getppm.pl http://people.apache.org/~chinthaka/ppm/1.ppm > 1.ppm");
	system("./getppm.pl http://people.apache.org/~chinthaka/ppm/2.ppm > 2.ppm");
	system("./getppm.pl http://people.apache.org/~chinthaka/ppm/3.ppm > 3.ppm");
	system("./getppm.pl http://people.apache.org/~chinthaka/ppm/4.ppm > 4.ppm");

	loadTexture("1.ppm", &Texture1, &TMap1);
	loadTexture("2.ppm", &Texture2, &TMap2);

} /* myInitData() */

/***************************************************************/
/*********************** MOUSE CALLBACKS ***********************/
/***************************************************************/

/* ------------------------------------------------------------------------ */
/* Mouse Button  UP/DOWN  or DOWN/UP transition only */
/* ------------------------------------------------------------------------ */
void myButton(int button, int state, int x, int y) {

	XmouseDown = x;
	YmouseDown = WindHeight - y - 1; /* Invert to right-handed system */

	/* This permits detection of left, middle, left+middle */
	if (state == GLUT_DOWN)
		mouseButtons |= button;
	else
		mouseButtons &= (~(button));

	/* bitmask with GLUT_ACTIVE_SHIFT, GLUT_ACTIVE_CTRL, GLUT_ACTIVE_ALT */
	KeybModifiers = glutGetModifiers();

	if (button == GLUT_LEFT_BUTTON || button == GLUT_MIDDLE_BUTTON) {
		if (state == GLUT_DOWN) {
			XmouseCurrent = XmousePrev = XmouseDown;
			YmouseCurrent = YmousePrev = YmouseDown;
		} else {
			/* at button up, invalidate: */
			XmouseCurrent = XmousePrev = XmouseDown = -1;
			YmouseCurrent = YmousePrev = YmouseDown = -1;
			KeybModifiers = 0;
		}
		DX = DY = 0;
	}
	glutPostRedisplay();
} /* myButton() */

/* ------------------------------------------------------------------------ */
/* Mouse motion WHILE BUTTON DOWN  */
/*  COMPLAINS  if you call glutGetModifiers()  here!! */
/* ------------------------------------------------------------------------ */
void myMotion(int x, int y) {
	XmouseCurrent = x;
	YmouseCurrent = WindHeight - y - 1; /* Invert to right-handed system */

	DX = XmouseCurrent - XmousePrev;
	DY = YmouseCurrent - YmousePrev;
	// printf("%d %d \n", DX, DY);

	XmousePrev = XmouseCurrent;
	YmousePrev = YmouseCurrent;

	switch (mouseButtons) {
	case 0x1: /* Left Mouse Button   */
		break;
	case 0x2: /* Middle Mouse Button */
		break;
	case 0x3: /* Left+Middle Mouse Button */
		break;
	case 0x4: /* Right Mouse Button (disabled with Menu)  */
		break;
	}

	glutPostRedisplay();
} /* myMotion(int x, int y) */

/* ------------------------------------------------------------------------ */
/* Handle Keyboard */
/* ------------------------------------------------------------------------ */
void myKeyboard(unsigned char key, int x, int y) {

	//	fprintf(stderr,"Pressed Key %s\n", key);

	/*  Option: To force all lower case */
	if (key >= 'A' && key <= 'Z')
		key = (key-'A') + 'a';

	switch (key) {
	case 'h':
		fprintf(stderr,"Help: \n\
  ESC or q: quit \n\
  t: toggle texture\n\
");
		break;
	case 't':
		if (TextureMode == 1) {
			loadTexture("1.ppm", &Texture1, &TMap1);
			loadTexture("2.ppm", &Texture2, &TMap2);
			TextureMode++;
		} else {
			loadTexture("3.ppm", &Texture1, &TMap1);
			loadTexture("4.ppm", &Texture2, &TMap2);
			TextureMode--;
		}
		break;
	case 'q':
	case 27: /* 27 -> ESC */
		fprintf(stderr,"Normal Exit.\n");
		exit(EXIT_SUCCESS);
		break;
	case 'w':
		if (InteractionMode == MY_MENU_PLAY && selectedPieceRow != 7) {
			selectedPieceRow++;
		}
		break;
	case 's':
		if (InteractionMode == MY_MENU_PLAY && selectedPieceRow != 0) {
			selectedPieceRow--;
		}
		break;
	case 'd':

		switch (InteractionMode) {
		case MY_MENU_OBJECT_ROTATE_Y:
			autoRotatingAngle++;
			break;
		case MY_MENU_OBJECT_ROTATE_Z:
			zRotationAngle++;
			break;
		case MY_MENU_OBJECT_ROTATE_X:
			xRotationAngle++;
			break;
		case MY_MENU_PLAY:
			if (selectedPieceColumn != 7) {
				selectedPieceColumn++;
			}
			break;
		}

		break;
	case 'a':

		switch (InteractionMode) {
		case MY_MENU_OBJECT_ROTATE_Y:
			autoRotatingAngle--;
			break;
		case MY_MENU_OBJECT_ROTATE_Z:
			zRotationAngle--;
			break;
		case MY_MENU_OBJECT_ROTATE_X:
			xRotationAngle--;
			break;
		case MY_MENU_OBJECT_TRANSLATE:
			TZ += 0.1;
			break;
		}

		if (selectedPieceColumn != 0) {
			selectedPieceColumn--;
		}

		break;
	case 'z':
		TZ -= 0.1;
		break;
	case ' ':
		if (pieceSelected == GL_TRUE) {
			targetLocationRow = selectedPieceRow;
			targetLocationColumn = selectedPieceColumn;
			pieceSelectedToBeMoved = GL_TRUE;
			pieceSelected = GL_FALSE;
		} else {
			sourceLocationRow = selectedPieceRow;
			sourceLocationColumn = selectedPieceColumn;
			pieceSelected = GL_TRUE;
		}
		break;

	default:
		fprintf(stderr,"Unhandled key: %c [%d] \n",key,key);
	}

	glutPostRedisplay();
} /* myKeyboard() */

/* -------------------------------------------------------------- */
/* myMenus() - callback for GLUT menus */
/* -------------------------------------------------------------- */
void myMenus(int value) {

	InteractionMode = value;

	if (value == MY_MENU_EXIT) {
		exit(0);
	} /* switch () */
	glutPostRedisplay(); /* make sure we Display according to changes */
} /* myMenus() */

/* -------------------------------------------------------------- */
/* initialize GLUT menus */
/* -------------------------------------------------------------- */
void myInitMenus(void) {

	int viewMenuId = glutCreateMenu(myMenus);

	// Add sub menu entry
	glutAddMenuEntry("Player One", MY_MENU_VIEW_PLAYER_ONE);
	glutAddMenuEntry("Player Two", MY_MENU_VIEW_PLAYER_TWO);
	glutAddMenuEntry("Side View", MY_MENU_VIEW_SIDE_VIEW);
	glutAddMenuEntry("Plan View", MY_MENU_VIEW_PLAN_VIEW);

	int objectMenuId = glutCreateMenu(myMenus);

	// Add sub menu entry
	glutAddMenuEntry("Rotate X", MY_MENU_OBJECT_ROTATE_X);
	glutAddMenuEntry("Rotate Y", MY_MENU_OBJECT_ROTATE_Y);
	glutAddMenuEntry("Rotate Z", MY_MENU_OBJECT_ROTATE_Z);
	glutAddMenuEntry("Translate", MY_MENU_OBJECT_TRANSLATE);

	glutCreateMenu(myMenus);

	glutAddMenuEntry("Move Camera", MY_MENU_CAMERA);
	//	glutAddMenuEntry("Move Light", MY_MENU_LIGHT);

	glutAddSubMenu("Views", viewMenuId);

	glutAddSubMenu("Object", objectMenuId);
	glutAddMenuEntry(" ", -1);
	glutAddMenuEntry("Play", MY_MENU_PLAY);
	glutAddMenuEntry(" ", -2);
	glutAddMenuEntry("Exit", MY_MENU_EXIT);

	glutAttachMenu(GLUT_RIGHT_BUTTON);
} /* myInitMenus() */

/***************************************************************/
/************************** INIT GL ****************************/
/***************************************************************/

void myInitGL(void) {
	GLfloat light_ambient[] = { 0.53, 0.0, 0.98, 1.0 };
	GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 };
	GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 };

	/* projection pipeline initialization */
	FOVangle = 60.0;
	FOVratio = 1.0;
	ClipNear = 0.1;
	ClipFar = 1000.0;
	XCamera = 2.68, YCamera = 1.86;
	ZCamera = 1.55;

	WindWidth = glutGet(GLUT_WINDOW_WIDTH);
	WindHeight = glutGet(GLUT_WINDOW_HEIGHT);
	glViewport(0, 0, WindWidth, WindHeight);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	FOVratio = (float)WindWidth / (float)WindHeight; /* keep viewed x/y ratio == 1 */
	/* instead of glFrustum(-1.0*ratio, 1.0*ratio, -1.0,1.0, FLength, 25.0);  */
	gluPerspective(FOVangle, FOVratio, ClipNear, ClipFar);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	/* Colors, Lighting, Material  initialization */
	glClearColor( 0.4f, 0.6f, 0.5f, 1.0f);
	glPointSize(1);
	glEnable(GL_POINT_SMOOTH);
	glLineWidth(1);
	glEnable(GL_LINE_SMOOTH);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	/* we do **not** want model materials to follow the current GL color: */
	glDisable(GL_COLOR_MATERIAL);

	glShadeModel(GL_SMOOTH); /* or GL_FLAT */
	glEnable(GL_NORMALIZE);
	glDepthFunc(GL_LESS);
	glEnable(GL_DEPTH_TEST);
	glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
	LightPos[0] = -12.6;
	LightPos[1] = 5.17;
	LightPos[2] = -10.93;
	LightPos[3] = 1.0;
	glLightfv(GL_LIGHT0, GL_POSITION, LightPos);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
} /* myInitGL() */

/***************************************************************/
/********************** DRAW FUNCTIONS *************************/
/***************************************************************/

void setMaterial1() {
	glEnable(GL_TEXTURE_2D);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glBindTexture(GL_TEXTURE_2D, Texture1);
	glFrontFace(GL_CW);
}

void setMaterial2() {
	glEnable(GL_TEXTURE_2D);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glBindTexture(GL_TEXTURE_2D, Texture2);
	glFrontFace(GL_CW);
}

void setChessPawnMaterial1() {
	GLfloat materialAmbience[] = { 0.53, 0.66, 0.68, 1.0 };
	GLfloat materialDiffuse[] = { 0.5, 0.0, 1.0, 1.0 };
	GLfloat materialSpecular[] = { 0.72, 1.0, 0.0, 1.0 };
	GLfloat materialSe = 10;

	glMaterialfv(GL_FRONT, GL_AMBIENT, materialAmbience);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, materialDiffuse);
	glMaterialfv(GL_FRONT, GL_SPECULAR, materialSpecular);
	glMaterialf(GL_FRONT, GL_SHININESS, materialSe);
}
void setChessPawnMaterial2() {
	GLfloat materialAmbience[] = { 0.53, 0.66, 0.68, 1.0 };
	GLfloat materialDiffuse[] = { 0.5, 0.0, 1.0, 1.0 };
	GLfloat materialSpecular[] = { 0.72, 1.0, 0.0, 1.0 };
	GLfloat materialSe = 100;

	glMaterialfv(GL_FRONT, GL_AMBIENT, materialDiffuse);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, materialSpecular);
	glMaterialfv(GL_FRONT, GL_SPECULAR, materialAmbience);
	glMaterialf(GL_FRONT, GL_SHININESS, materialSe);
}

void setSelectedBoxMaterial() {
	GLfloat materialAmbience[] = { 0.11, 0.83, 0.68, 1.0 };
	GLfloat materialDiffuse[] = { 0.5, 0.5, 1.0, 1.0 };
	GLfloat materialSpecular[] = { 0.72, 0.0, 0.5, 1.0 };
	GLfloat materialSe = 50;

	glMaterialfv(GL_FRONT, GL_AMBIENT, materialDiffuse);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, materialSpecular);
	glMaterialfv(GL_FRONT, GL_SPECULAR, materialAmbience);
	glMaterialf(GL_FRONT, GL_SHININESS, materialSe);
}

void drawRect() {
	glBegin(GL_POLYGON);
	glTexCoord2d(0, 0);
	glVertex3d(0.1, 0.1, 0.1);
	glTexCoord2d(1, 0);
	glVertex3d(0.1+boxSize, 0.1, 0.1);
	glTexCoord2d(1, 1);
	glVertex3d(0.1+boxSize, 0.1, 0.1+boxSize);
	glTexCoord2d(0, 1);
	glVertex3d(0.1, 0.1, 0.1+boxSize);
	glEnd();
}

/**
 * This will set the environment to the given column and row of the chess board.
 * column number will be the x cordinate while row number will be z cordinate
 */
void setLocationOnChessBoard(GLint row, GLint column) {
	glTranslated(chessBoardStartX + boxSize/2 + boxSize * column, 0.0,
			chessBoardStartZ + boxSize/2 + boxSize * row);
}

void drawChessBoard() {

	int x, y;

	for (y = 0; y < 8; y++) {

		for (x = 0; x < 8; x++) {

			if (y == selectedPieceRow && x == selectedPieceColumn) {
				glFrontFace(GL_CCW);
				glDisable(GL_TEXTURE_2D);
			} else if (((x+y) % 2) == 0) {
				setMaterial1();
			} else {
				setMaterial2();
			}

			glPushMatrix();
			glTranslated(chessBoardStartX + boxSize * x, 0.0, chessBoardStartZ
					+ y * boxSize);
			drawRect();
			glPopMatrix();

		}
	}

	chessBoardCenterOfMass.x = (GLdouble) (chessBoardStartX + boxSize * 4);
	chessBoardCenterOfMass.z = (GLdouble) (chessBoardStartZ + boxSize * 4);
	chessBoardCenterOfMass.y = 0.0;

	// draw chess board


	glFrontFace(GL_CCW);
	glDisable(GL_TEXTURE_2D);
}

static void _Timer(int value) {
	/* increment angle */
	autoRotatingAngle++;

	/* send redisplay event */
	glutPostRedisplay();

	/* call this function again in 10 milliseconds */
	glutTimerFunc(1, _Timer, 0);
}

void drawLightSource() {
	glDisable(GL_LIGHTING);
	glPushMatrix();
	glTranslatef(LightPos[0], LightPos[1], LightPos[2]);
	glColor3f(1.0, 1.0, 1.0);
	glutSolidCube(0.1);
	glPopMatrix();
	glEnable(GL_LIGHTING);
}

void drawSelectedSquare() {
	setMaterial1();
	glPushMatrix();
	setLocationOnChessBoard(selectedPieceRow, selectedPieceColumn);
	glBegin(GL_POLYGON);
	glTexCoord2d(0, 0);
	glVertex3d(-boxSize/2, 0.1, 0.1);
	glTexCoord2d(1, 0);
	glVertex3d(0.1+boxSize, 0.1, 0.1);
	glTexCoord2d(1, 1);
	glVertex3d(0.1+boxSize, 0.1, 0.1+boxSize);
	glTexCoord2d(0, 1);
	glVertex3d(0.1, 0.1, 0.1+boxSize);
	glEnd();
}

GLboolean isLegalMove() {

	// are these different types. i.e. both are white?
	if (chessBoardStandings[targetLocationRow][targetLocationColumn]
			== chessBoardStandings[sourceLocationRow][sourceLocationColumn]) {
		fprintf(stderr, "Illegal move. Can not override same type pieces\n");
		return GL_FALSE;
	}

	// place to implement movement rules
	GLuint chessPieceName =
			chessBoardLayout[sourceLocationRow][sourceLocationColumn];

	if (chessPieceName == EMPTY_LOCATION) {
		return GL_FALSE;
	}

	switch (chessPieceName) {
	case CHESS_PAWN:
		break;
	case CHESS_ROOK:
		break;
	case CHESS_KING:
		break;
	case CHESS_QUEEN:
		break;
	case CHESS_BISHOP:
		break;
	case CHESS_KNIGHT:
		break;
	}

	return GL_TRUE;

}

void drawChessPieces() {

	// move the chess piece, if there is sth to be moved
	if (pieceSelectedToBeMoved == GL_TRUE && isLegalMove() == GL_TRUE) {

		chessBoardLayout[targetLocationRow][targetLocationColumn]
				= chessBoardLayout[sourceLocationRow][sourceLocationColumn];
		chessBoardLayout[sourceLocationRow][sourceLocationColumn]
				= EMPTY_LOCATION;

		chessBoardStandings[targetLocationRow][targetLocationColumn]
				= chessBoardStandings[sourceLocationRow][sourceLocationColumn];
		chessBoardStandings[sourceLocationRow][sourceLocationColumn]
				= EMPTY_LOCATION;

		pieceSelectedToBeMoved = GL_FALSE;
	}

	setMaterial2();
	int x =0, y = 0;
	for (x = 0; x < 8; x++) {
		for (y = 0; y < 8; y++) {
			GLuint chessPieceIndex = chessBoardLayout[x][y];
			GLuint chessPieceType = chessBoardStandings[x][y];
			if (chessPieceIndex != 100) {

				// set the material first
				if (chessPieceType == WHITE_PIECE) {
					setMaterial1();
				} else {
					setMaterial2();
				}

				// draw the relevant piece
				glPushMatrix();
				setLocationOnChessBoard(x, y);

				// if the piece is selected, lift it
				if (x == sourceLocationRow && y == sourceLocationColumn) {
					glTranslated(0.0, 0.4, 0.0);
				}

				switch (chessPieceIndex) {
				case CHESS_PAWN:
					pawn.draw();
					break;
				case CHESS_ROOK:
					rook.draw();
					break;
				case CHESS_KING:
					king.draw();
					break;
				case CHESS_QUEEN:
					queen.draw();
					break;
				case CHESS_BISHOP:
					glScaled(0.7, 0.7, 0.7);
					bishop.draw();
					break;
				case CHESS_KNIGHT:
					glScaled(0.6, 0.6, 0.6);
					knight.draw();
					break;
				}

				glPopMatrix();
			}
		}
	}
}

/* ------------------------------------------------------------------------ */
void myDisplay(void) {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glLoadIdentity();

	/* viewing transformation */
	if (InteractionMode == MY_MENU_CAMERA) {
		/* move camera */
		if ((KeybModifiers & GLUT_ACTIVE_SHIFT) == 0) {
			XCamera = XCamera + 0.01*(GLdouble)DX;
			YCamera = YCamera + 0.01*(GLdouble)DY;
		} else {
			ZCamera = ZCamera + 0.01*DX;
		}
	}

	/* lights positioning */
	if (InteractionMode == MY_MENU_LIGHT) {
		/* move camera */
		if ((KeybModifiers & GLUT_ACTIVE_SHIFT) == 0) {
			LightPos[0] = LightPos[0] + 0.01*(GLdouble)DX;
			LightPos[1] = LightPos[1] + 0.01*(GLdouble)DY;
		} else {
			LightPos[2] = LightPos[2] + 0.01*(GLdouble)DY;
		}

	}

	/* modeling transformations follow,  */
	/*placing 3D models in the scene */
	if (InteractionMode == MY_MENU_OBJECT_TRANSLATE) {

		if (mouseButtons == 0x0) {
			if ((KeybModifiers & GLUT_ACTIVE_SHIFT) == 0) {
				// translate along x and y directions
				TX = TX + 0.01*(GLdouble)DX;
				TY = TY + 0.01*(GLdouble)DY;
			} else {
				// traslate along z direction
				TZ = TZ + 0.01*DX;
			}
		}

	}

	fprintf(stderr, " Camera : %f, %f, %f\n", XCamera, YCamera, ZCamera);
	fprintf(stderr, " Object : %f, %f, %f\n", TX, TY, TZ);

	fprintf(stderr, " XYZ Rotation : %f, %f, %f\n", xRotationAngle, autoRotatingAngle, zRotationAngle);

	if (InteractionMode == MY_MENU_OBJECT) {
		if (mouseButtons == 0x2) {
			if ((KeybModifiers & GLUT_ACTIVE_SHIFT) == 0) {
				// rotate around Z axis
				zRotationAngle += DX;
			} else {
				// rolling ball rotation
				dx = DX/300.0;
				dy = DY/300.0;
				dr = sqrt(dx*dx + dy*dy);
				rx = -dy/dr;
				ry = dx/dr;
				rotationAngle += 180*asin(dr/sqrt(1 + dr*dr))/M_PI;
			}
		}
	} else if (InteractionMode == MY_MENU_VIEW_SIDE_VIEW) {
		xRotationAngle = -4.0;
		autoRotatingAngle = 84.0;
		zRotationAngle = 2.0;
	} else if (InteractionMode == MY_MENU_VIEW_PLAN_VIEW) {
		XCamera = 2.66;
		YCamera = 2.32;
		ZCamera = 1.55;
		TX = 0.95;
		TY = 2.55;
		TZ = -2.8;
		xRotationAngle = 73.0;
		autoRotatingAngle = 39.0;
		zRotationAngle = 362.0;
	} else if (InteractionMode == MY_MENU_VIEW_PLAYER_ONE) {
		XCamera = 2.65;
		YCamera = 2.32;
		ZCamera = 1.55;
		TX = 0.95;
		TY = 1.14;
		TZ = -2.93;
		xRotationAngle = zRotationAngle = autoRotatingAngle = 0.0;
	} else if (InteractionMode == MY_MENU_VIEW_PLAYER_TWO) {
		XCamera = 2.65;
		YCamera = 2.32;
		ZCamera = 1.55;
		TX = 1.17;
		TY = 1.05;
		TZ = -2.93;
		xRotationAngle = zRotationAngle = 0.0;
		autoRotatingAngle = 180.0;
	}
	/* Push/Pop Doesn't matter here due to LoadIdentity, but 
	 could matter in more complicated scene. */
	glTranslatef(-XCamera, -YCamera, -ZCamera);

	glTranslatef(TX, TY, TZ);
	glTranslated(chessBoardCenterOfMass.x, chessBoardCenterOfMass.y,
			chessBoardCenterOfMass.z);
	glRotated(autoRotatingAngle, 0.0, 1.0, 0.0);
	glRotated(zRotationAngle, 0.0, 0.0, 1.0);
	glRotated(xRotationAngle, 1.0, 0.0, 1.0);
	glRotated(rotationAngle, rx, ry, 1.0);
	glTranslated(-chessBoardCenterOfMass.x, -chessBoardCenterOfMass.y,
			-chessBoardCenterOfMass.z);
	glLightfv(GL_LIGHT0, GL_POSITION, LightPos);
	drawLightSource();
	drawChessBoard();
	drawChessPieces();
	//	drawSelectedSquare();

	glutSwapBuffers();
} /* myDisplay() */

/***************************************************************/
/*********************** GLUT FUNCTIONS ************************/
/***************************************************************/

/* ------------------------------------------------------------------------ */
void myReshape(int w, int h) {
	w = MY_MAX(w,128);
	h = MY_MAX(h,128);
	WindHeight = h;
	WindWidth = w;
	glutReshapeWindow(w, h);
	glViewport(0, 0, w, h);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	FOVratio = (float)w / (float)h; /* keep world coord x/y ratio == 1 */
	gluPerspective(FOVangle, FOVratio, ClipNear, ClipFar);
	glMatrixMode(GL_MODELVIEW);
} /* myReshape() */

/* ------------------------------------------------------------------------ */
void myVisibility(int state) {
	/* ------------------------------------------------------------------------ */
	if (state == GLUT_VISIBLE) {
		glutPostRedisplay();
	}
} /* myVisibility() */

/* ------------------------------------------------------------------------ */

